//! 解析struct各字段，生成select field、join、pk等信息
use proc_macro2::Ident;
use syn::{
    parse_str, Data, DataStruct, Field, Fields, FieldsNamed, Lit, Meta, MetaList, MetaNameValue,
    NestedMeta, Type,
};

/// 类型转换类别
#[derive(Clone, Debug)]
pub(super) enum FieldConvertType {
    /// Option<T> -> T
    Option,
    /// String -> &str
    String,
    /// 无转换
    None,
}
impl FieldConvertType {
    /// 把 Option<T> 转化成 T
    /// 把 String 转换为&str
    pub(super) fn convert_some_type(ty: &mut Type) -> FieldConvertType {
        use syn::{
            AngleBracketedGenericArguments, GenericArgument, Path, PathArguments, PathSegment,
            TypePath,
        };
        //dbg!(ty);
        if let Type::Path(TypePath {
            qself: None,
            path: Path {
                leading_colon,
                segments,
            },
        }) = ty
        {
            if leading_colon.is_none() && segments.len() == 1 {
                if let Some(PathSegment {
                    ident,
                    arguments:
                        PathArguments::AngleBracketed(AngleBracketedGenericArguments { args, .. }),
                }) = segments.first()
                {
                    if let (1, Some(GenericArgument::Type(t))) = (args.len(), args.first()) {
                        if ident == "Option" {
                            // 转换Option<T>到T
                            *ty = t.clone();
                            return FieldConvertType::Option;
                        }
                    }
                } else if let Some(PathSegment {
                    ident,
                    arguments: PathArguments::None,
                }) = segments.first()
                {
                    if ident == "String" {
                        //dbg!(ident);
                        *ty = parse_str::<Type>("&str").unwrap();
                        return FieldConvertType::String;
                    }
                }
            }
        }
        FieldConvertType::None
    }
}

/// struct相关orm解析信息
#[derive(Clone)]
#[allow(dead_code)]
pub(super) struct TinyOrmField {
    /// 对应struct字段标识
    pub(super) field_ident: Ident,
    /// 对应字段类型
    pub(super) field_type: Type,
    /// 类型转换类别
    pub(super) field_type_convert_type: FieldConvertType,
    /// 对应数据库表字段名称
    pub(super) field_db_name: String,
    /// 是否插入时自动增长
    pub(super) auto_pk: bool,
    /// 是否自动生成该字段的self.orm_update_字段名称方法
    pub(super) is_orm_update: bool,
    /// 是否自动生成该字段的Self::orm_query_with字段名称方法
    pub(super) is_orm_query: bool,
    /// 是否自动生成该字段的Self::orm_get_with字段名称方法
    pub(super) is_orm_get: bool,
    /// 可选的query order_by 语句
    pub(super) order_by_sql:Option<String>,
}
#[derive(Clone)]
#[allow(dead_code)]
pub(super) struct TinyOrmJoinField {
    /// 字段信息
    pub(super) field: TinyOrmField,
    /// join时的select字段信息
    pub(super) select_field: String,
    /// select的join部分
    pub(super) join_sql: String,
    /// 用于与当前表字段进行连接的对应类型的连接字段
    pub(super) link_id: String,
    /// 用于与当前表字段进行连接的对应类型的连接字段类型
    pub(super) link_id_type: String,
    /// 跳过相关方法自动生成，true则不自动生成相关方法
    pub(super) is_skip_method: bool,
}
/// struct字段解析数据
#[allow(dead_code)]
#[derive(Clone)]
pub(super) enum TinyOrmFieldData {
    /// 主键
    Pk(TinyOrmField),
    /// 普通字段
    Normal(TinyOrmField),
    /// JOIN字段
    Join(TinyOrmJoinField),
}

impl TinyOrmFieldData {
    /// 获取属性值
    ///
    /// # 参数说明
    ///
    /// * meta 对应属性meta
    /// * key 要获取的属性名称
    ///
    /// # 返回
    ///
    /// 返回对应属性key对应的值
    fn get_value_from_meta(meta: &Meta, key: &str) -> Option<String> {
        if let Meta::List(MetaList { nested, .. }) = meta {
            // 读取orm_pk的name值
            nested.iter().find_map(|nested_meta| match nested_meta {
                NestedMeta::Meta(Meta::NameValue(MetaNameValue {
                    path,
                    lit: Lit::Str(lit_str),
                    ..
                })) if path.is_ident(key) => Some(lit_str.value()),
                _ => None,
            })
        } else {
            None
        }
    }
    /// 查证某一属性值是否定义
    ///
    /// # 参数说明
    ///
    /// * meta 对应属性meta
    /// * key 要获取的属性名称
    ///
    /// # 返回
    ///
    /// 返回对应属性key对应的值
    #[allow(dead_code)]
    fn exist_from_meta(meta: &Meta, key: &str) -> bool {
        if let Meta::List(MetaList { nested, .. }) = meta {
            // 读取orm_pk的name值
            nested
                .iter()
                .any(|nested_meta| matches!(nested_meta, NestedMeta::Meta(Meta::NameValue(MetaNameValue { path, .. })) if path.is_ident(key)))
        } else {
            false
        }
    }
    /// 解析结构体字段的属性信息
    fn parse_field_attr(field: &Field) -> Option<TinyOrmFieldData> {
        // 对应字段名称
        let field_ident = field.ident.as_ref().unwrap();
        let mut field_type = field.ty.clone();
        let field_convert_type = FieldConvertType::convert_some_type(&mut field_type);
        // 判断该字段是否有orm_update属性
        let is_orm_update = field
            .attrs
            .iter()
            .any(|attr| attr.path.is_ident("orm_update"));
        // 判断该字段是否有orm_query属性
        let is_orm_query = field
            .attrs
            .iter()
            .any(|attr| attr.path.is_ident("orm_query"));
        // 提取orm_query的order_by语句
        let order_by_sql = field.attrs.iter().find_map(|attr| {
            // 根据标签类型，解析字段orm信息
            if attr.path.is_ident("orm_query") {
                if let Ok(meta) = attr.parse_meta(){
                    return Self::get_value_from_meta(&meta, "order_by");
                }
            }
            None
        });
        
        // 判断该字段是否有orm_get属性
        let is_orm_get = field.attrs.iter().any(|attr| attr.path.is_ident("orm_get"));
        // dbg!(&field_type);
        // 获取orm_pk属性相关信息
        let parsed_field = field.attrs.iter().find_map(|attr| {
            // 根据标签类型，解析字段orm信息
            if attr.path.is_ident("orm_pk") || attr.path.is_ident("orm_field") {
                // 转换为meta
                let meta = attr.parse_meta().expect("orm_pk|orm_field:转换属性为meta");
                let db_name = Self::get_value_from_meta(&meta, "name")
                    .unwrap_or_else(|| field.ident.as_ref().unwrap().to_string());
                let auto_pk =
                    Self::get_value_from_meta(&meta, "auto") == Some(String::from("true"));
                let orm_field = TinyOrmField {
                    field_ident: field_ident.clone(),
                    field_type: field_type.clone(),
                    field_type_convert_type: field_convert_type.clone(),
                    field_db_name: db_name,
                    auto_pk,
                    is_orm_update,
                    is_orm_query,
                    is_orm_get,
                    order_by_sql:order_by_sql.clone(),
                };
                // 主键字段和定义表字段名称的字段
                if attr.path.is_ident("orm_pk") {
                    Some(TinyOrmFieldData::Pk(orm_field))
                } else {
                    Some(TinyOrmFieldData::Normal(orm_field))
                }
            } else if attr.path.is_ident("orm_join") {
                let meta = attr.parse_meta().expect("orm_join:转换属性为meta");
                let db_name = Self::get_value_from_meta(&meta, "name")
                    .unwrap_or_else(|| field.ident.as_ref().unwrap().to_string());
                let is_skip_method =
                    Self::get_value_from_meta(&meta, "skip_method") == Some(String::from("true"));
                let orm_field = TinyOrmField {
                    field_ident: field_ident.clone(),
                    field_type: field_type.clone(),
                    field_type_convert_type: field_convert_type.clone(),
                    field_db_name: db_name,
                    auto_pk: false,
                    is_orm_update,
                    is_orm_query,
                    is_orm_get,
                    order_by_sql:order_by_sql.clone(),
                };
                // join字段
                let select_field = Self::get_value_from_meta(&meta, "select_field")
                    .expect("获取join对应的select_field");
                let join_sql = Self::get_value_from_meta(&meta, "join")
                    .unwrap_or_else(|| panic!("获取join对应的join,字段名称:{}", &field_ident));
                let link_id = Self::get_value_from_meta(&meta, "link_id")
                    .unwrap_or_else(|| panic!("获取join对应的link_id,字段名称:{}", &field_ident));
                let link_id_type =
                    Self::get_value_from_meta(&meta, "link_id_type").unwrap_or_else(|| {
                        panic!("获取join对应的link_id_type,字段名称:{}", &field_ident)
                    });
                let join_field = TinyOrmJoinField {
                    field: orm_field,
                    select_field,
                    join_sql,
                    link_id,
                    link_id_type,
                    is_skip_method,
                };
                Some(TinyOrmFieldData::Join(join_field))
            } else {
                None
            }
        });

        (!field
            .attrs
            .iter()
            .any(|attr| attr.path.is_ident("orm_ignore")))
        .then(|| {
            parsed_field.unwrap_or_else(|| {
                let orm_field = TinyOrmField {
                    field_ident: field_ident.clone(),
                    field_type: field_type.clone(),
                    field_type_convert_type: field_convert_type.clone(),
                    field_db_name: field.ident.as_ref().unwrap().to_string(),
                    auto_pk: false,
                    is_orm_update,
                    is_orm_query,
                    is_orm_get,
                    order_by_sql,
                };
                TinyOrmFieldData::Normal(orm_field)
            })
        })
    }
    /// 解析各字段的orm信息
    pub(super) fn parse_orm_field_info(ast: &syn::DeriveInput) -> Vec<TinyOrmFieldData> {
        if let Data::Struct(DataStruct {
            fields: Fields::Named(FieldsNamed { named, .. }, ..),
            ..
        }) = &ast.data
        {
            // 解析主键字段信息（主键字段，字段类型，对应数据库表中主键字段名称）
            let data = named
                .into_iter()
                .filter_map(Self::parse_field_attr)
                .collect::<Vec<_>>();
            return data;
        }
        panic!("无法解析属性信息")
    }
}
